# LeetCode 35、搜索插入位置
# 一、题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:
输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:
输入: nums = [1], target = 0
输出: 0
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums
为无重复元素的升序排列数组-10^4 <= target <= 10^4
# 二、题目解析
# 三、参考代码
# 1、Java 代码
// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 搜索插入位置(35):https://leetcode-cn.com/problems/search-insert-position/
class Solution {
public int searchInsert(int[] nums, int target) {
// left 为当前区间最左侧的元素,可以获取到
int left = 0;
// right 为当前区间最右侧的元素,可以获取到
int right = nums.length - 1;
// 在 while 循环里面,left 不断的 ++,而 right 不断的 --
// 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
while(left <= right) {
// left + (right - left) / 2 和 (left + right) / 2 的结果相同
// 但是使用 left + (right - left) / 2 可以防止由于 left 、right 同时太大,导致相加的结果溢出的问题
// 比如数据 int 的最大值为 2,147,483,647
// 此时,left 为 2,147,483,640
// 此时,right 为 2,147,483,646
// 两者直接相加超过了 2,147,483,647,产生了溢出
int mid = left + (right - left) / 2;
// 中间的元素和目标值 target 相等,直接返回下标即可,不需要插入
if(nums[mid] == target) {
// 直接返回下标即可
return mid;
// 中间的元素大于了目标值 target
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ mid , right ] 这个区间中的所有元素都大于目标值 target
// 因此,缩小查找区间为 [ left , mid - 1]
} else if(nums[mid] > target) {
// 也就是说缩小之后的区间最右侧位置为 mid - 1
right = mid - 1;
// 中间的元素小于了目标值 target
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ left , mid ] 这个区间中的所有元素都小于目标值 target
// 因此,缩小查找区间为 [ mid + 1 , right]
} else if(nums[mid] < target){
// 也就是说缩小之后的区间最左侧位置为 mid + 1
left = mid + 1;
}
}
// 执行到这里,说明退出了 while 循环,在 nums 里面没有找到和目标值 target 相同的元素,需要将 target 插入到 nums 中
// 在上面 while 循环每次的操作中,都会使得 [ left , right ] 的这个区间中的元素减少,直到 left = right + 1 出现区间不存在元素位置
// 在出现这种情况之前,也就是 while 循环操作的最后一次时,left = right,那么此时计算的 mid = left = right
// 此时 nums[mid] 左边的数全部小于目标值 target ,nums[mid] 右边的数全部大于目标值 target
// 1、如果 nums[mid] 大于 target,那么接下来 right 会向左移动,即 right = left - 1,此时搜索区间不存在
// 那么说明 target 应该插入到 nums[mid] 的前一个位置,也就是顶替了 nums[mid] 的位置,nums[mid] 后面所有元素都向后移动
// 而此时 left 指向的就是 nums[mid] 这个元素,所以 left 就是插入的位置
// 2、如果 nums[mid] 小于 target,那么接下来 left 会向右移动,即 left = right + 1,此时搜索区间不存在
// 那么说明 target 应该插入到 nums[mid] 的后一个位置,而 left 向后移动了一次,这个位置就是插入位置
return left;
}
}
# 2、C++ 代码
// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 搜索插入位置(35):https://leetcode-cn.com/problems/search-insert-position/
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
// left 为当前区间最左侧的元素,可以获取到
int left = 0;
// right 为当前区间最右侧的元素,可以获取到
int right = nums.size() - 1;
// 在 while 循环里面,left 不断的 ++,而 right 不断的 --
// 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
while(left <= right) {
// left + (right - left) / 2 和 (left + right) / 2 的结果相同
// 但是使用 left + (right - left) / 2 可以防止由于 left 、right 同时太大,导致相加的结果溢出的问题
// 比如数据 int 的最大值为 2,147,483,647
// 此时,left 为 2,147,483,640
// 此时,right 为 2,147,483,646
// 两者直接相加超过了 2,147,483,647,产生了溢出
int mid = left + (right - left) / 2;
// 中间的元素和目标值 target 相等,直接返回下标即可,不需要插入
if(nums[mid] == target) {
// 直接返回下标即可
return mid;
// 中间的元素大于了目标值 target
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ mid , right ] 这个区间中的所有元素都大于目标值 target
// 因此,缩小查找区间为 [ left , mid - 1]
} else if(nums[mid] > target) {
// 也就是说缩小之后的区间最右侧位置为 mid - 1
right = mid - 1;
// 中间的元素小于了目标值 target
// 1、数组为有序数组,比如为递增数组
// 2、数组中不存在重复元素
// 由于该数组存在以上两个特点,所以 [ left , mid ] 这个区间中的所有元素都小于目标值 target
// 因此,缩小查找区间为 [ mid + 1 , right]
} else if(nums[mid] < target){
// 也就是说缩小之后的区间最左侧位置为 mid + 1
left = mid + 1;
}
}
// 执行到这里,说明退出了 while 循环,在 nums 里面没有找到和目标值 target 相同的元素,需要将 target 插入到 nums 中
// 在上面 while 循环每次的操作中,都会使得 [ left , right ] 的这个区间中的元素减少,直到 left = right + 1 出现区间不存在元素位置
// 在出现这种情况之前,也就是 while 循环操作的最后一次时,left = right,那么此时计算的 mid = left = right
// 此时 nums[mid] 左边的数全部小于目标值 target ,nums[mid] 右边的数全部大于目标值 target
// 1、如果 nums[mid] 大于 target,那么接下来 right 会向左移动,即 right = left - 1,此时搜索区间不存在
// 那么说明 target 应该插入到 nums[mid] 的前一个位置,也就是顶替了 nums[mid] 的位置,nums[mid] 后面所有元素都向后移动
// 而此时 left 指向的就是 nums[mid] 这个元素,所以 left 就是插入的位置
// 2、如果 nums[mid] 小于 target,那么接下来 left 会向右移动,即 left = right + 1,此时搜索区间不存在
// 那么说明 target 应该插入到 nums[mid] 的后一个位置,而 left 向后移动了一次,这个位置就是插入位置
return left;
}
};
# 3、Python 代码
# 登录 AlgoMooc 官网获取更多算法图解
# https://www.algomooc.com
# 作者:程序员吴师兄
# 代码有看不懂的地方一定要私聊咨询吴师兄呀
# 二分查找(704):https://leetcode-cn.com/problems/binary-search/
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
# left 为当前区间最左侧的元素,可以获取到
left = 0
# right 为当前区间最右侧的元素,可以获取到
right = len(nums) - 1
# 在 while 循环里面,left 不断的 ++,而 right 不断的 --
# 当 [ left , right ] 这个区间不存在元素的时候,才跳出 while 循环,也就是当 left > right 时跳出循环
# 即当 left = right + 1 时,搜索区间没有元素了
# 由于 left 和 right 相遇的时候指向同一个元素,这个元素是存在于 [ left , right] 区间,这个区间就一个元素
# 所以此时 while 循环的判断可以使用等号
while left <= right :
# left + (right - left) / 2 和 (left + right) / 2 的结果相同
# 但是使用 left + (right - left) / 2 可以防止由于 left 、right 同时太大,导致相加的结果溢出的问题
# 比如数据 int 的最大值为 2,147,483,647
# 此时,left 为 2,147,483,640
# 此时,right 为 2,147,483,646
# 两者直接相加超过了 2,147,483,647,产生了溢出
mid = left + (right - left) // 2
# 中间的元素和目标值 target 相等,直接返回下标即可
if nums[mid] == target :
# 直接返回下标即可
return mid
# 中间的元素大于了目标值 target
# 1、数组为有序数组,比如为递增数组
# 2、数组中不存在重复元素
# 由于该数组存在以上两个特点,所以 [ mid , right ] 这个区间中的所有元素都大于目标值 target
# 因此,缩小查找区间为 [ left , mid - 1]
elif nums[mid] > target :
# 也就是说缩小之后的区间最右侧位置为 mid - 1
right = mid - 1
# 中间的元素小于了目标值 target
# 1、数组为有序数组,比如为递增数组
# 2、数组中不存在重复元素
# 由于该数组存在以上两个特点,所以 [ left , mid ] 这个区间中的所有元素都小于目标值 target
# 因此,缩小查找区间为 [ mid + 1 , right]
elif nums[mid] < target :
# 也就是说缩小之后的区间最左侧位置为 mid + 1
left = mid + 1
# 执行到这里,说明退出了 while 循环,在 nums 里面没有找到和目标值 target 相同的元素,需要将 target 插入到 nums 中
# 在上面 while 循环每次的操作中,都会使得 [ left , right ] 的这个区间中的元素减少,直到 left = right + 1 出现区间不存在元素位置
# 在出现这种情况之前,也就是 while 循环操作的最后一次时,left = right,那么此时计算的 mid = left = right
# 此时 nums[mid] 左边的数全部小于目标值 target ,nums[mid] 右边的数全部大于目标值 target
# 1、如果 nums[mid] 大于 target,那么接下来 right 会向左移动,即 right = left - 1,此时搜索区间不存在
# 那么说明 target 应该插入到 nums[mid] 的前一个位置,也就是顶替了 nums[mid] 的位置,nums[mid] 后面所有元素都向后移动
# 而此时 left 指向的就是 nums[mid] 这个元素,所以 left 就是插入的位置
# 2、如果 nums[mid] 小于 target,那么接下来 left 会向右移动,即 left = right + 1,此时搜索区间不存在
# 那么说明 target 应该插入到 nums[mid] 的后一个位置,而 left 向后移动了一次,这个位置就是插入位置
return left
# 四、复杂度分析
时间复杂度:O(logn),其中 n 是给定版本的数量。
空间复杂度:O(1)。我们只需要常数的空间保存若干变量。